package com.gitee.wsl.math.random

import kotlin.math.ln
import kotlin.math.sqrt
import kotlin.random.Random

fun randomDouble(min: Double=0.0, max: Double=1.0): Double = Random.nextDouble(min, max)

fun Random.nextFloat(start:Float, end:Float) = nextDouble(start.toDouble(),end.toDouble()).toFloat()

/**
 * Returns a random number from a Gaussian distribution using the Box-Muller transform.
 *
 * @return A random number from a Gaussian distribution.
 * @see [Box-Muller transform](https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform)
 */
fun Random.nextGaussian(): Double {
    var u: Double
    var v: Double
    var s: Double
    do {
        u = nextDouble() * 2 - 1
        v = nextDouble() * 2 - 1
        s = u * u + v * v
    } while (s >= 1 || s == 0.0)
    val mul = sqrt(-2.0 * ln(s) / s)
    return u * mul
}

fun Random.nextGaussianFloat() = nextGaussian().toFloat()
fun Random.nextGaussianFloat(mu: Float, sigma: Float) = mu + nextGaussianFloat() * sigma

fun Random.nextDoubleInclusive() = (this.nextInt(0x1000001).toDouble() / 0x1000000.toDouble())

operator fun Random.get(min: Double, max: Double): Double = min + nextDouble() * (max - min)
operator fun Random.get(min: Float, max: Float): Float = min + nextFloat() * (max - min)
operator fun Random.get(min: Int, max: Int): Int = min + nextInt(max - min)
operator fun Random.get(range: IntRange): Int = range.first + this.nextInt(range.last - range.first + 1)
operator fun Random.get(range: LongRange): Long = range.first + this.nextLong() % (range.last - range.first + 1)

fun Random.ints(): Sequence<Int> = sequence { while (true) yield(nextInt()) }
fun Random.ints(from: Int, until: Int): Sequence<Int> = sequence { while (true) yield(nextInt(from, until)) }
fun Random.ints(range: IntRange): Sequence<Int> = ints(range.first, range.last + 1)

fun Random.doubles(): Sequence<Double> = sequence { while (true) yield(nextDouble()) }
fun Random.floats(): Sequence<Float> = sequence { while (true) yield(nextFloat()) }
